package org.jugile.restapi.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;
import org.jugile.util.*;

import org.jugile.restapi.common.*;



public abstract class PathItemBase extends Entity {
	static Logger log = Logger.getLogger(PathItemBase.class);
	
	public DOH getDoh() { return new DOH("PathItem",getId()); }

	public void reindex(Domain d) {}
	
	// --- fields ---

	private String name;
	public String getName() { return name; }
	public PathItem setName(String v) { name = v; d()._modified(this); return (PathItem)this; }
 	private String desc;
	public String getDesc() { return desc; }
	public PathItem setDesc(String v) { desc = v; d()._modified(this); return (PathItem)this; }
 	private String str;
	public String getStr() { return str; }
	public PathItem setStr(String v) { str = v; d()._modified(this); return (PathItem)this; }
 	private String regexp;
	public String getRegexp() { return regexp; }
	public PathItem setRegexp(String v) { regexp = v; d()._modified(this); return (PathItem)this; }
 	private String tmplDir;
	public String getTmplDir() { return tmplDir; }
	public PathItem setTmplDir(String v) { tmplDir = v; d()._modified(this); return (PathItem)this; }
 	private String modelDir;
	public String getModelDir() { return modelDir; }
	public PathItem setModelDir(String v) { modelDir = v; d()._modified(this); return (PathItem)this; }
 	private String pkg;
	public String getPkg() { return pkg; }
	public PathItem setPkg(String v) { pkg = v; d()._modified(this); return (PathItem)this; }
 	private String controller;
	public String getController() { return controller; }
	public PathItem setController(String v) { controller = v; d()._modified(this); return (PathItem)this; }
 	private String dcontroller;
	public String getDcontroller() { return dcontroller; }
	public PathItem setDcontroller(String v) { dcontroller = v; d()._modified(this); return (PathItem)this; }
 	private String redirect;
	public String getRedirect() { return redirect; }
	public PathItem setRedirect(String v) { redirect = v; d()._modified(this); return (PathItem)this; }
 	private String var;
	public String getVar() { return var; }
	public PathItem setVar(String v) { var = v; d()._modified(this); return (PathItem)this; }
 	private String border;
	public String getBorder() { return border; }
	public PathItem setBorder(String v) { border = v; d()._modified(this); return (PathItem)this; }
 	private PathItemType type;
	public PathItemType getType() { return type; }
	public PathItem setType(PathItemType v) { type = v; d()._modified(this); return (PathItem)this; }
 	private String condition;
	public String getCondition() { return condition; }
	public PathItem setCondition(String v) { condition = v; d()._modified(this); return (PathItem)this; }
  

 
	// --- connections ---

	// --- self ref ---
	private PathItem parent;
	public PathItem getParent() { return parent; }
	public PathItem setParent(PathItem v) { mapParent(v);	d()._modified(this); return (PathItem)this; }
	public void removeChild(PathItemBase v) { if (v != null) v.setParent(null); }
	public void addChild(PathItemBase v) { if (v != null) ((PathItemBase)v).mapParent(this); }
	
	private void mapParent(PathItemBase v) {
		if (v == null) {
			if (parent != null) ((PathItemBase)parent).children.remove((PathItem)this);
			parent = null;
			return;
		}
		v.children.add((PathItem)this);
		parent = (PathItem)v;
	}
	
	private Bos<PathItem> children = new Bos<PathItem>();
	public PathItemCollection getChildren() { return new PathItemCollection(children); }

 
 	// ----- tree methods -----
	public String getIndent(String str) {
		int level = getLevel();
		String res = "";
		for (int i = 0; i < level; i++) res += str;
		return res;		
	}
	public String getTxtIndent() { return getIndent("  "); }
	public String getHtmlIndent() { return getIndent("&nbsp;&nbsp;"); }
	
	public int getLevel() {
		return getLevel(0);
	}
	protected int getLevel(int l) {
		PathItem t = getParent();
		if (t == null) return l;
		return t.getLevel(l+1);
	}	
	
	public boolean isRoot() { 
		return getParent() == null; 
	}
	
	public PathItem getRoot() {
		PathItem p = getParent();
		if (p == null) return (PathItem)this;
		return p.getRoot();
	}
	public List<PathItem> getTreeAsList() {
		List<PathItem> res = new ArrayList<PathItem>();
		getFlatTree(res);
		return res;
	}
	protected void getFlatTree(List<PathItem> res) {
		res.add((PathItem)this);
		for (PathItem o : getChildren().sort("id")) o.getFlatTree(res);
	}
		
 
 

	// --- 1 - n ----

	private OperationCollection cOperations = new OperationCollection();
	public OperationCollection getOperations() { return cOperations.clone(); }
	public void addOperation(Operation o) { if (o != null) o.setPathItem((PathItem)this); }
	public void removeOperation(Operation o) { if (o != null) o.setPathItem(null); }
	protected void _addOperation(Operation o) { cOperations.add(o); }
	protected void _removeOperation(Operation o) { cOperations.remove(o); }
	public void addOperationDistinct(Operation o) { if (!cOperations.contains(o)) cOperations.add(o); }
	

	private PartCollection cParts = new PartCollection();
	public PartCollection getParts() { return cParts.clone(); }
	public void addPart(Part o) { if (o != null) o.setPathItem((PathItem)this); }
	public void removePart(Part o) { if (o != null) o.setPathItem(null); }
	protected void _addPart(Part o) { cParts.add(o); }
	protected void _removePart(Part o) { cParts.remove(o); }
	public void addPartDistinct(Part o) { if (!cParts.contains(o)) cParts.add(o); }
	

	private VarCollection cVars = new VarCollection();
	public VarCollection getVars() { return cVars.clone(); }
	public void addVar(Var o) { if (o != null) o.setPathItem((PathItem)this); }
	public void removeVar(Var o) { if (o != null) o.setPathItem(null); }
	protected void _addVar(Var o) { cVars.add(o); }
	protected void _removeVar(Var o) { cVars.remove(o); }
	public void addVarDistinct(Var o) { if (!cVars.contains(o)) cVars.add(o); }
	

 

	// --- n - 1 ----

	private Api api;
	public Api getApi() { return api; }
	public PathItem setApi(Api v) {
		d()._modified(this);
		return mapApi(v);
	}
	protected PathItem mapApi(Api v) { 
		if (api != null) api._removePathItem((PathItem)this);
		if (v != null) v._addPathItem((PathItem)this);
		api = v;
		return (PathItem)this;
	}

 

	// --- n - n ----
 
	// --- life cycle methods ---
	public void delete() { delete(true); }
	public void delete(boolean ondelete) {
		if (ondelete) this.onDelete();
		if (getParent() != null) getParent().removeChild((PathItem)this);
 		Api o1 = getApi();
		if (o1 != null) o1.removePathItem((PathItem)this);  
 		for (Operation o : getOperations()) {
			o.setPathItem(null);
		}
		for (Part o : getParts()) {
			o.setPathItem(null);
		}
		for (Var o : getVars()) {
			o.setPathItem(null);
		}
  		d().deletePathItem(getId());
 		d()._deleted(this);
	}

	public void onDelete() {} // override 
	public void onLoad(IDomain d) {} // override
	public void onWrite() {} // override
	public void onDeleteEvent() {} // override
	public void onSave() {} // override - not implemented yet
	
	public String toString() {
		String res = "";
		res += nn(name) + ";";
 		res += nn(desc) + ";";
 		res += nn(str) + ";";
 		res += nn(regexp) + ";";
 		res += nn(tmplDir) + ";";
 		res += nn(modelDir) + ";";
 		res += nn(pkg) + ";";
 		res += nn(controller) + ";";
 		res += nn(dcontroller) + ";";
 		res += nn(redirect) + ";";
 		res += nn(var) + ";";
 		res += nn(border) + ";";
 		res += nn(type) + ";";
 		res += nn(condition) + ";";
  		return res;
	}
	
	
	public final static String[] _flds = {
		"name",
		"desc",
		"str",
		"regexp",
		"tmplDir",
		"modelDir",
		"pkg",
		"controller",
		"dcontroller",
		"redirect",
		"var",
		"border",
		"type",
		"condition",
 		"parent",
 		"Api",
 	};
	public static String[] flds() { return _flds; }
	
	public void parseName(String v) {
	    if ("-".equals(v)) v = null;
		setName(v); return;
 	}
	public void parseDesc(String v) {
	    if ("-".equals(v)) v = null;
		setDesc(v); return;
 	}
	public void parseStr(String v) {
	    if ("-".equals(v)) v = null;
		setStr(v); return;
 	}
	public void parseRegexp(String v) {
	    if ("-".equals(v)) v = null;
		setRegexp(v); return;
 	}
	public void parseTmplDir(String v) {
	    if ("-".equals(v)) v = null;
		setTmplDir(v); return;
 	}
	public void parseModelDir(String v) {
	    if ("-".equals(v)) v = null;
		setModelDir(v); return;
 	}
	public void parsePkg(String v) {
	    if ("-".equals(v)) v = null;
		setPkg(v); return;
 	}
	public void parseController(String v) {
	    if ("-".equals(v)) v = null;
		setController(v); return;
 	}
	public void parseDcontroller(String v) {
	    if ("-".equals(v)) v = null;
		setDcontroller(v); return;
 	}
	public void parseRedirect(String v) {
	    if ("-".equals(v)) v = null;
		setRedirect(v); return;
 	}
	public void parseVar(String v) {
	    if ("-".equals(v)) v = null;
		setVar(v); return;
 	}
	public void parseBorder(String v) {
	    if ("-".equals(v)) v = null;
		setBorder(v); return;
 	}
	public void parseType(String v) {
		if ("-".equals(v)) v = null;
		setType(PathItemType.map(v));
 	}
	public void parseCondition(String v) {
	    if ("-".equals(v)) v = null;
		setCondition(v); return;
 	}
 
	public void parseParent(String id) {
		if ("-".equals(id)) setParent(null);
		else setParent( (PathItem)d().getOrCreate("PathItem", id));
	}
 
	public void parseApi(String id) {
		if ("-".equals(id)) setApi(null);
		else setApi( (Api)d().getOrCreate("Api", id));
	}
 
	// ----- db persistence -----

	protected static String _getSelectFlds() {
		Buffer buf = new Buffer();
		buf.add("id_f,ts,host,vers");
		for (String fld : _flds) {
			buf.add(","+fld+"_f");
		}
		return buf.toString();
	}
	protected String _getUpdateFlds() {
		Buffer buf = new Buffer();
		for (int i = 0; i < _flds.length; i++) {
			if (i > 0) buf.add(",");
			buf.add(_flds[i]+"_f=?");
		}
		return buf.toString();		
	}
	
	protected void _readFlds(Domain d, List row) {
		_modified = row.get(1)==null?null:new Time((java.util.Date)row.get(1));
		String host = (String)row.get(2);
		setVersion((Integer)row.get(3));
		name = (String)row.get(4);
		desc = (String)row.get(5);
		str = (String)row.get(6);
		regexp = (String)row.get(7);
		tmplDir = (String)row.get(8);
		modelDir = (String)row.get(9);
		pkg = (String)row.get(10);
		controller = (String)row.get(11);
		dcontroller = (String)row.get(12);
		redirect = (String)row.get(13);
		var = (String)row.get(14);
		border = (String)row.get(15);
		type = row.get(16) == null? PathItemType.map(0):PathItemType.map((Integer)row.get(16));
		condition = (String)row.get(17);
 		mapParent(row.get(18)==null?null:getOrCreate(d,(Integer)row.get(18)));
 		mapApi(row.get(19)==null?null:Api.getOrCreate(d,(Integer)row.get(19)));
 
		// call onLoad
		onLoad(d);
		setVirgin(false);
	}

	
	protected void _writeFlds(DBConnection c) throws Exception {
		onWrite();
		// is it insert or update?
		if (isVirgin()) {
			c.prepare("insert into "+ _tablename+" set id_f=?,host=?,vers=?," + _getUpdateFlds());
			c.param(Long.parseLong(getId()));
			c.param(Domain.getHostName());
			c.param(1L);

			setWriteParams(c);
			
			try {
				c.execute();
			} catch(Exception se) {
				log.error("Failed to insert writeFlds PathItem: " + getId());
				throw se;
			}
			setVersion(1L);
		} else {
			c.prepare("update "+ _tablename+" set host=?,vers=?," + _getUpdateFlds() + " where id_f=? AND vers=?");
			c.param(Domain.getHostName());
			c.param(getVersion()+1L); // increment db version
			
			setWriteParams(c);
			
			c.param(Long.parseLong(getId()));
			c.param(getVersion());
			try {
				int res = c.execute();
				if (res != 1) {
					fail("object not found: PathItem " + getId() + " " + getVersion());
				}
			} catch(Exception se) {
				log.error("Failed to update writeFlds PathItem: " + getId());
				throw se;
			}
			// increment mem object versions when db commit is ok
		}
	}

	private void setWriteParams(DBConnection c) throws Exception {
		c.param(trim(getName(),300));
		c.param(trim(getDesc(),300));
		c.param(trim(getStr(),300));
		c.param(trim(getRegexp(),300));
		c.param(trim(getTmplDir(),300));
		c.param(trim(getModelDir(),300));
		c.param(trim(getPkg(),300));
		c.param(trim(getController(),300));
		c.param(trim(getDcontroller(),300));
		c.param(trim(getRedirect(),300));
		c.param(trim(getVar(),300));
		c.param(trim(getBorder(),300));
		c.param(getType());
		c.param(trim(getCondition(),300));
 		c.param(getParent());
 		c.param(getApi());
 	}

	private final static String _tablename = "pathitem_t";
	protected String _getTableName() { return _tablename; }

	protected static int _readAllUpdates(Domain d, DBConnection c, Time ts, String host) throws Exception {
		if (host == null) {
			c.prepare("select "+PathItem._getSelectFlds()+" from "+_tablename+" where ts >= ?");			
			c.param(ts);
		} else {
			c.prepare("select "+PathItem._getSelectFlds()+" from "+_tablename+" where ts >= ? AND host != ?");			
			c.param(ts);
			c.param(host);
		}
		int count = 0;
		for (List row : c.select()) {
			long id = (Integer)row.get(0);
			PathItem o = getOrCreate(d,id);
			o._readFlds(d,row);
			count++;
		}
		return count;
	}
	
	private Time _modified;
	public Time getModifiedTime() { return _modified; }
	
	protected static PathItem getOrCreate(Domain d, long id) {
		PathItem o = d.getPathItem(id);
		if (o == null) o = d._createEmptyPathItem(id);
		return o;
	}

}